package miniworld;

import homeMadeExceptions.ScatterNetException;
import simulationEngine.SimulationEngine;
import datatypes.BluetoothDeviceAddress;
import datatypes.Connection;
import datatypes.Host;
import datatypes.LinkMode;

public class GildedCage {

	// homie pages the peripherie and trys to connect the peripherie Node
	/**
	 * returns true if connections is established
	 */
	public static boolean connectingArea(Host homie, BluetoothDeviceAddress peripherie) {
		
		SimulationEngine engine = SimulationEngine.getEngine(homie.getSimulationEngineID());
		Host per = engine.getHostList().getHost(peripherie);

		//connect yourself?
		if (homie.getBd_Addr().getAddress().equals(peripherie.getAddress())) {
			metric.CentralObservableUnit.networkAnSingleConnectFailed(homie, per, engine.getSimulationEnviroment().getClock().getCurrentTime());
			return false;
		}
		
		// unusable constalltions
		if ((per.getLM() == LinkMode.SLAVE && homie.getLM() == LinkMode.SLAVE)) {
			metric.CentralObservableUnit.networkAnSingleConnectFailed(homie, per, engine.getSimulationEnviroment().getClock().getCurrentTime());
			return false;
		}
		if ((per.getLM() == LinkMode.MASTER && homie.getLM() == LinkMode.MASTER)) {
			metric.CentralObservableUnit.networkAnSingleConnectFailed(homie, per, engine.getSimulationEnviroment().getClock().getCurrentTime());
			return false;
		}

		if (!engine.getMiniworld().getRoster().areHostsInRange(per.getID(), homie.getID())){
			metric.CentralObservableUnit.networkAnSingleConnectFailed(homie, per, engine.getSimulationEnviroment().getClock().getCurrentTime());
			return false;
		}
		
		// peripherienode is master and my directivy is not to be everytime
		// Master
		if (homie.getLM() != LinkMode.MASTER && engine.getMiniworld().getScatternet().isHostAlreadyMaster(per)) {
			try {
				engine.getMiniworld().propagateNewConnection(homie, peripherie);
				createConnection(engine, peripherie, homie.getBd_Addr());
				//in this simulation there is no weighted Connections needed,... constant weight therefor is randomly choosen as 5
				engine.getMiniworld().getPhysicalNetwork().addEdge(homie, per, 5);
				metric.CentralObservableUnit.networkAnSingleConnectionEstablished(homie, per, engine.getSimulationEnviroment().getClock().getCurrentTime());
				return true;
			} catch (ScatterNetException e) {
				// Nothing to do, there are other possible ways for get a
				// connection
				metric.CentralObservableUnit.networkAnSingleConnectException(homie, per, engine.getSimulationEnviroment().getClock().getCurrentTime());
			}
		}

		// i am already master and peripherynode has not the directive to be
		// everytime master
		if (per.getLM() != LinkMode.MASTER && engine.getMiniworld().getScatternet().isHostAlreadyMaster(homie)) {
			try {
				engine.getMiniworld().propagateNewConnection(per, homie.getBd_Addr());
				createConnection(engine, homie.getBd_Addr(), peripherie);
				//in this simulation there is no weighted Connections needed,... constant weight therefor is randomly choosen as 5
				engine.getMiniworld().getPhysicalNetwork().addEdge(homie, per, 5);
				metric.CentralObservableUnit.networkAnSingleConnectionEstablished(homie, per, engine.getSimulationEnviroment().getClock().getCurrentTime());
				return true;
			} catch (ScatterNetException e) {
				// do nothin only for readable code ,...
				metric.CentralObservableUnit.networkAnSingleConnectException(homie, per, engine.getSimulationEnviroment().getClock().getCurrentTime());
			}
		}

		// however both nodes are not master, or an directive of the other node
		// made a connection
		// on current Master-Slave Formation impossible
		// try to establish new Master (peripheriNode)
		if (per.getLM() == LinkMode.MASTER && homie.getLM() != LinkMode.MASTER) {
			try {
				// raises exception, if already master
				engine.getMiniworld().propagateNewMaster(per, per.getRoutingSocket().maxSlaves);
				// if master is created, there should be no way, to become an
				// exception by this propagation
				engine.getMiniworld().propagateNewConnection(homie, peripherie);
				createConnection(engine, peripherie, homie.getBd_Addr());
				//in this simulation there is no weighted Connections needed,... constant weight therefor is randomly choosen as 5
				engine.getMiniworld().getPhysicalNetwork().addEdge(homie, per, 5);
				metric.CentralObservableUnit.networkAnSingleConnectionEstablished(homie, per, engine.getSimulationEnviroment().getClock().getCurrentTime());
				return true;
			} catch (ScatterNetException e) {
				// ignore, because there are other chances to create an
				// connection
				metric.CentralObservableUnit.networkAnSingleConnectException(homie, per, engine.getSimulationEnviroment().getClock().getCurrentTime());
			}
		}

		if (per.getLM() == LinkMode.NONE && homie.getLM() != LinkMode.MASTER) {
			try {
				// raises exception, if already master
				engine.getMiniworld().propagateNewMaster(per, per.getRoutingSocket().maxSlaves);
				// if master is created, there should be no way, to become an
				// exception by this propagation
				engine.getMiniworld().propagateNewConnection(homie, peripherie);
				createConnection(engine, peripherie, homie.getBd_Addr());
				//in this simulation there is no weighted Connections needed,... constant weight therefor is randomly choosen as 5
				engine.getMiniworld().getPhysicalNetwork().addEdge(homie, per, 5);
				metric.CentralObservableUnit.networkAnSingleConnectionEstablished(homie, per, engine.getSimulationEnviroment().getClock().getCurrentTime());
				return true;
			} catch (ScatterNetException e) {
				// ignore, because there are other chances to create an
				// connection
				metric.CentralObservableUnit.networkAnSingleConnectException(homie, per, engine.getSimulationEnviroment().getClock().getCurrentTime());
			}
		}

		if (per.getLM() != LinkMode.MASTER && homie.getLM() == LinkMode.NONE) {

			// its the same to do for Linkmode Master and None
			try {
				// raises exception, if already master
				engine.getMiniworld().propagateNewMaster(homie, homie.getRoutingSocket().maxSlaves);
				// if master is created, there should be no way, to become an
				// exception by this propagation
				engine.getMiniworld().propagateNewConnection(per, homie.getBd_Addr());
				createConnection(engine, homie.getBd_Addr(), peripherie);
				//in this simulation there is no weighted Connections needed,... constant weight therefor is randomly choosen as 5
				engine.getMiniworld().getPhysicalNetwork().addEdge(homie, per, 5);
				metric.CentralObservableUnit.networkAnSingleConnectionEstablished(homie, per, engine.getSimulationEnviroment().getClock().getCurrentTime());
				return true;
			} catch (ScatterNetException e) {
				// ignore
				metric.CentralObservableUnit.networkAnSingleConnectException(homie, per, engine.getSimulationEnviroment().getClock().getCurrentTime());
			}
		}

		metric.CentralObservableUnit.networkAnSingleConnectFailed(homie, per, engine.getSimulationEnviroment().getClock().getCurrentTime());
		return false;

	}

	private static void createConnection(SimulationEngine engine, BluetoothDeviceAddress master, BluetoothDeviceAddress slave) {
		int timeStepID = engine.getSimulationEnviroment().getClock().getCurrentTime();
		Connection masterSlave = new Connection(slave, true, timeStepID);
		Connection slaveMaster = new Connection(master, false, timeStepID);
		engine.getHostList().addConnection(master, masterSlave);
		engine.getHostList().addConnection(slave, slaveMaster);
	}
}
